﻿#region Header

/*
Behavioral Rating of Dancing Human Crowds based on Motion Patterns
By

Pascal Hauser 
Dipl. Ing. in Informatik, Hochschule für Technik Rapperswil, 2006
Master Thesis, Hochschule für Technik Rapperswil, 2008-2010

and

Raphael Gfeller
Dipl. Ing. in Informatik, Hochschule für Technik Rapperswil, 2006
Master Thesis, Hochschule für Technik Rapperswil, 2008-2010

*/

#endregion

#region Usings

using System;
using System.Collections.Generic;
using paravili.Services;
using Sebarf.Diagnostics.Interfaces;
using Sebarf.Services.Interfaces;

#endregion

namespace paravili.Steps {
	/// <summary>
	/// Based on given conditions, a boolean result is returned
	/// for example value > 50 -> true
	/// </summary>
	public class ConditionEvaluater : ProcessStepWithMeasurement<Object> {
		#region Public Properties

		[ServiceRequest]
		public IStatisticValuesLocatorService StatisticValuesLocatorService { get; set; }

		[ConfigurabelBooleanValue(Name = "is enable")]
		public bool IsEnable { get; set; }

		[ConfigurabelStringValue(Name = "Condition 1 in the format: value < > [number] -> output")]
		public string Condition1 {
			get { return m_condition1; }
			set {
				m_condition1 = value;
				ParseConditions();
			}
		}

		[ConfigurabelStringValue(Name = "Condition 2 in the format: value < > [number] -> output")]
		public string Condition2 {
			get { return m_condition2; }
			set {
				m_condition2 = value;
				ParseConditions();
			}
		}

		[ConfigurabelStringValue(Name = "Condition 3 in the format: value < > [number] -> output")]
		public string Condition3 {
			get { return m_condition3; }
			set {
				m_condition3 = value;
				ParseConditions();
			}
		}

		[ConfigurabelStringValue(Name = "StatisticValue to observer")]
		public string StatisticValueField { get; set; }

		[ConfigurabelStringValue(Name = "StatisticValue for the result")]
		public string StatisticValueOutputField { get; set; }

		#endregion

		#region Public Methods

		public ConditionEvaluater() {
			StatisticValueField = "Movement@Other";
			Condition1 = "value < 50 && value > 0 --> Bad Emotion";
			Condition2 = "value < 80 && value >= 50 --> Normal Emotion";
			Condition3 = "value < 150 && value >= 80 --> Good Emotion";
			StatisticValueOutputField = "Out";
			IsEnable = true;
		}

		protected override object OnProcess(object toProcess) {
			if (!IsEnable) {
				return toProcess;
			}
			StatisticValueEntry staticValue = StatisticValuesLocatorService[StatisticValueField];
			if (staticValue != null) {
				object v = staticValue.Value;
				if (v is int) {
					var intValue = (int)v;
					foreach (AndBasedLinkedConditions c in m_conditions) {
						if (c.IsTrue(intValue)) {
							StatisticValuesProviderService.SetStatisticValue(StatisticValueOutputField, c.TrueConditionResult.Value);
							return toProcess;
						}
					}
				}
			}
			return toProcess;
		}

		#endregion

		#region Private Methods

		private void ParseConditions() {
			m_conditions.Clear();
			AddConditionToCollection(m_conditions, Condition1);
			AddConditionToCollection(m_conditions, Condition2);
			AddConditionToCollection(m_conditions, Condition3);
		}

		private void AddConditionToCollection(IList<AndBasedLinkedConditions> collection, string condition) {
			try {
				AndBasedLinkedConditions c = m_converter.ConvertToCondition(condition);
				if (c != null) {
					collection.Add(c);
				}
			}
			catch (FormatException) {
				// ignore format exception
				Logger.WriteDebug("FormatException:" + condition);
			}
		}

		#endregion

		#region Private Fields

		private readonly List<AndBasedLinkedConditions> m_conditions = new List<AndBasedLinkedConditions>();
		private readonly StringToConditionConverter m_converter = new StringToConditionConverter();
		private string m_condition1;
		private string m_condition2;
		private string m_condition3;

		#endregion
	}

	public class StringToConditionConverter {
		public AndBasedLinkedConditions ConvertToCondition(string input) {
			if (string.IsNullOrEmpty(input)) {
				return null;
			}
			input = input.Trim();
			if (input == string.Empty) {
				return null;
			}
			int resultStartIndex = input.IndexOf("-->");
			if (resultStartIndex < 0) {
				return null;
			}
			string result = input.Substring(resultStartIndex);
			input = input.Substring(0, input.Length - result.Length);
			return new AndBasedLinkedConditions(input, result);
		}
	}

	/// <summary>
	/// represents a condition that can contains other conditions, these are evaluated AND based
	/// </summary>
	public class AndBasedLinkedConditions : Conditon {
		#region Public Properties

		public ConditionResult TrueConditionResult { get; set; }

		#endregion

		#region Public Methods

		public AndBasedLinkedConditions(string condition, string result) {
			TrueConditionResult = new ConditionResult(result);
			string andSeperator = "&&";
			do {
				int index = condition.IndexOf(andSeperator);
				if (index < 0) {
					if (!string.IsNullOrEmpty(condition.Trim())) {
						m_conditions.Add(new SingleMathematicalCondition(condition));
					}

					condition = string.Empty;
				}
				else {
					string t = condition.Substring(0, index);

					condition = condition.Substring(index + andSeperator.Length);
					if (string.IsNullOrEmpty(t.Trim())) {
						continue;
					}
					m_conditions.Add(new SingleMathematicalCondition(t));
				}
			} while (condition.Trim().Length > 0);
		}

		public override bool IsTrue(int value) {
			foreach (Conditon c in m_conditions) {
				if (!c.IsTrue(value)) {
					return false;
				}
			}
			return true;
		}

		#endregion

		#region Private Fields

		private readonly List<Conditon> m_conditions = new List<Conditon>();

		#endregion
	}

	/// <summary>
	/// represents a condition that can evaluate a single expression like 5 > 2
	/// </summary>
	public class SingleMathematicalCondition : Conditon {
		#region Public Properties

		public ConditionType Type { get; private set; }
		public bool IsValueLeft { get; private set; }
		public bool IsValueRight { get; private set; }
		public double LeftValue { get; private set; }
		public double RightValue { get; private set; }

		#endregion

		#region Public Methods

		public SingleMathematicalCondition(string input) {
			if (input.Contains("<>")) {
				Type = ConditionType.IsNotEqual;
			}
			else if (input.Contains("<=")) {
				Type = ConditionType.IsSmallerEqual;
			}
			else if (input.Contains(">=") || input.Contains("=>")) {
				Type = ConditionType.IsGreaterEqual;
			}
			else if (input.Contains("==")) {
				Type = ConditionType.IsEqual;
			}
			else if (input.Contains("<=") || input.Contains("=<")) {
				Type = ConditionType.IsSmallerEqual;
			}
			else if (input.Contains("=")) {
				Type = ConditionType.IsEqual;
			}
			else if (input.Contains("<")) {
				Type = ConditionType.IsSmaller;
			}
			else if (input.Contains(">")) {
				Type = ConditionType.IsGreater;
			}
			else {
				throw new FormatException();
			}
			input = input.Replace("<", ";");
			input = input.Replace(">", ";");
			input = input.Replace("=", ";");
			string[] values = input.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
			if (values.Length != 2) {
				throw new FormatException();
			}
			IsValueLeft = values[0].Trim() == "value";
			IsValueRight = values[1].Trim() == "value";

			if (!IsValueLeft) {
				LeftValue = int.Parse(values[0]);
			}
			if (!IsValueRight) {
				RightValue = int.Parse(values[1]);
			}
		}

		public override bool IsTrue(int value) {
			if (IsValueLeft) {
				LeftValue = value;
			}
			if (IsValueRight) {
				RightValue = value;
			}
			switch (Type) {
				case ConditionType.IsEqual:
					return LeftValue == RightValue;
				case ConditionType.IsGreater:
					return LeftValue > RightValue;
				case ConditionType.IsGreaterEqual:
					return LeftValue >= RightValue;
				case ConditionType.IsNotEqual:
					return LeftValue != RightValue;
				case ConditionType.IsSmaller:
					return LeftValue < RightValue;
				case ConditionType.IsSmallerEqual:
					return LeftValue <= RightValue;
			}
			return false;
		}

		#endregion

		#region ConditionType enum

		public enum ConditionType {
			IsSmaller,
			IsSmallerEqual,
			IsGreater,
			IsGreaterEqual,
			IsEqual,
			IsNotEqual
		}

		#endregion
	}

	/// <summary>
	/// abstract definition of a condition
	/// </summary>
	public abstract class Conditon {
		public abstract bool IsTrue(int value);
	}

	/// <summary>
	///  result of a condition
	/// </summary>
	public class ConditionResult {
		public ConditionResult(string value) {
			Value = value.Replace("-->", "").Trim();
		}

		public string Value { get; set; }
	}
}